﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Script.Serialization;


namespace MvcTemplates
{
    public static class TemplateHelpers
    {
        public static MvcHtmlString C( this HtmlHelper helper, Expression<Func<Templates, CloneableTagBuilder>> tagBuilder, 
            TagRenderMode renderMode = TagRenderMode.Normal, object additionalHtmlAttirbutes = null, string additionalIdAppender = null, string propertyName = "" )
        {
            var tb = (CloneableTagBuilder) tagBuilder.Compile().Invoke( helper.GetProfile() ).Clone();

            RouteValueDictionary attributesToMerge;

            if ( additionalHtmlAttirbutes is RouteValueDictionary )
            {
                attributesToMerge = ( RouteValueDictionary )additionalHtmlAttirbutes;
            }
            else
            {
                attributesToMerge = new RouteValueDictionary( additionalHtmlAttirbutes );    
            }
            
            MergeAttributes( attributesToMerge, tb.Attributes );
            
            var text = ExpressionHelper.GetExpressionText( tagBuilder );
            
            var sanitizedValue = GenerateCssAndIdFriendlyValue( text, tb.IdAttributeDotReplacement );
            
            if ( !string.IsNullOrEmpty( additionalIdAppender ) )
                sanitizedValue += string.Format( "{0}{1}", tb.IdAttributeDotReplacement, additionalIdAppender );

            var currentPropertyId = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId( propertyName );
            
            if ( !tb.Attributes.ContainsKey( "id" ) )
            {
                if ( !string.IsNullOrEmpty( currentPropertyId ) )
                {
                    tb.Attributes["id"] = string.Format( "{0}{1}{2}", currentPropertyId, tb.IdAttributeDotReplacement, sanitizedValue ); 
                }
                else
                {
                    tb.Attributes["id"] = sanitizedValue;
                }    
            }
            
            tb.AddCssClass( sanitizedValue );            
            return MvcHtmlString.Create( tb.ToString( renderMode ) );
        }

        private static string GenerateCssAndIdFriendlyValue( string name, string idAttributeDotReplacement )
        {
            var tb = new TagBuilder( "b" );
            tb.IdAttributeDotReplacement = idAttributeDotReplacement;
            tb.GenerateId( name );
            return tb.Attributes["id"];
        }

        public static Templates GetProfile( this HtmlHelper helper )
        {
            var modelMetadata = helper.ViewData.ModelMetadata;
            var profileAttr = modelMetadata.GetTemplateData<ProfileAttribute>();
            
            if ( profileAttr == null && modelMetadata.ContainerType != null )
            {
                profileAttr = modelMetadata.ContainerType
                    .GetCustomAttributes( typeof ( ProfileAttribute ), true )
                    .OfType<ProfileAttribute>().FirstOrDefault();
            }

            if ( profileAttr == null )
            {
                return Templates.DefaultProfile;
            }

            return Templates.Profiles[profileAttr.Name];
        }

        internal static void MergeAttributes( IDictionary<string, object> source, IDictionary<string, string> destination )
        {
            foreach( var value in source )
            {
                if ( value.Key == "class" )
                {
                    if ( !destination.ContainsKey( "class" ) )
                    {
                        destination["class"] = value.Value.ToString();
                    }
                    else
                    {
                        destination["class"] += string.Format( " {0}", value.Value );
                    }                    
                }
                else
                {
                    destination[value.Key] = value.Value.ToString();
                }
            }    
        }

        internal static void MergeAttributes( IDictionary<string, object> source, IDictionary<string, object> destination )
        {
            foreach( var value in source )
            {
                if ( value.Key == "class" )
                {
                    if ( !destination.ContainsKey( "class" ) )
                    {
                        destination["class"] = value.Value;
                    }
                    else
                    {
                        destination["class"] += string.Format( " {0}", value.Value );
                    }                    
                }
                else
                {
                    destination[value.Key] = value.Value;
                }
            }  
        }

        internal static object GetModelStateValue( this HtmlHelper helper, string key, Type destinationType )
        {
            ModelState modelState;
            if ( helper.ViewData.ModelState.TryGetValue(key, out modelState)) {
                if (modelState.Value != null) {
                    return modelState.Value.ConvertTo(destinationType, null /* culture */);
                }
            }
            return null;
        }

        public static MvcHtmlString ToJson( object value )
        {
            var javascriptSerializer = new JavaScriptSerializer();
            var serializedValue = javascriptSerializer.Serialize( value );
            var mvcHtmlString = MvcHtmlString.Create( serializedValue );
            return mvcHtmlString;
        }

        internal const string Tab = "\t";

        public static string GetResourceText( Type resourceType, string resourceName)
        {
            if ( resourceType == null )
                throw new NullReferenceException( "resourceType must be specified" );
            if ( string.IsNullOrEmpty( resourceName ) )
                throw new NullReferenceException( "resourceName must be specified" );
            
            PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static);
            
            if (property == null)
            {
                    throw new InvalidOperationException(string.Format("Resource Type Does Not Have Property"));
            }
            
            if (property.PropertyType != typeof(string))
            {
                    throw new InvalidOperationException(string.Format("Resource Property is Not String Type"));
            }

            return ( string )property.GetValue( null, null );            
        }        
    }
}
